package QC.MessageBox.service;

import QC.MessageBox.beans.AppUser;
import QC.MessageBox.beans.CustomerServ;
import QC.MessageBox.beans.WXPublic;
import QC.MessageBox.beans.WebUserMap;
import QC.MessageBox.common.Constants;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.redis.RedisClient;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by Andaren on 2017/4/6.
 * Used for: Redis服务
 */
public class RedisService {

    private RedisClient redis;

    public static final String REIDS_KEY_WX_PUBLIC = "wx_publics";

    public static final String REDIS_KEY_WX_USER = "wx_users";
    // 从Redis中获取app用户talkId的key
    public static final String REDIS_KEY_APP_USER_TALKID = "app_users";
    // 客服集合
    public static final String REDIS_KEY_APP_CS = "app_customer_service";
    // 网页用户记录
    public static final String REDIS_KEY_WEB_USER_CHAT_ID = "web_user_chat_id";

    public static AtomicLong REDIS_RANDOM_CS_ID = new AtomicLong(0);

    public RedisService (RedisClient redisClient) {
        this.redis = redisClient;
    }

    /**
     * 获取相应的key对应的value
     * @param key
     * @return
     */
    public Future<String> get (String key) {
        Future<String> fut = Future.future();
        redis.get(key, res -> {
            if (res.succeeded()) {
                String value = res.result();
                fut.complete(value);
            } else if (res.failed()) {
                fut.fail(res.cause());
            }
        });
        return fut;
    }

    /**
     * 设置键值对
     * @param key
     * @param value
     * @return
     */
    public Future<Void> set(String key, String value) {
        Future<Void> fut = Future.future();
        redis.set(key, value, as -> {
            if (as.succeeded()) {
                // 设置过期时间
                redis.expire(key, Constants.REDIS_CACHE_MAX_TIME, expireAr -> {
                    if (expireAr.failed()) {
                        fut.fail(expireAr.cause());
                    } else {
                        fut.complete();
                    }
                });
            } else {
                fut.fail(as.cause());
            }
        });
        return fut;
    }

    /**
     * 删除某个key
     * @param key
     * @return
     */
    public Future<Void> delete(String key) {
        Future<Void> delFut = Future.future();
        redis.del(key, delAs -> {
            if (delAs.failed()) {
                delFut.fail(delAs.cause());
            } else {
                delFut.complete();
            }
        });
        return delFut;
    }

    public Future<Void> deleteAllKey () {
        Future delAllFut = Future.future();

        Future<Void> delAllPubFut = Future.future();
        redis.keys(REIDS_KEY_WX_PUBLIC + "*", pub-> {
            if (pub.failed()) {
                delAllPubFut.fail(delAllPubFut.cause());
            } else {
                for (Object key : pub.result().getList()) {
                    redis.del(String.valueOf(key), Future.future());
                }
                delAllPubFut.complete();
            }
        });
        Future<Void> delAllAppUserFut = Future.future();
        redis.keys(REDIS_KEY_APP_USER_TALKID + "*", pub-> {
            if (pub.failed()) {
                delAllAppUserFut.fail(delAllPubFut.cause());
            } else {
                for (Object key : pub.result().getList()) {
                    redis.del(String.valueOf(key), Future.future());
                }
                delAllAppUserFut.complete();
            }
        });
        Future<Void> delAllWxUserFut = Future.future();
        redis.keys(REDIS_KEY_WX_USER + "*", pub-> {
            if (pub.failed()) {
                delAllWxUserFut.fail(delAllPubFut.cause());
            } else {
                for (Object key : pub.result().getList()) {
                    redis.del(String.valueOf(key), Future.future());
                }
                delAllWxUserFut.complete();
            }
        });
        Future<Void> delAllCSFut = Future.future();
        redis.keys(REDIS_KEY_APP_CS + "*", pub-> {
            if (pub.failed()) {
                delAllCSFut.fail(delAllPubFut.cause());
            } else {
                for (Object key : pub.result().getList()) {
                    redis.del(String.valueOf(key), Future.future());
                }
                delAllCSFut.complete();
            }
        });
        CompositeFuture.all(
                delAllPubFut, delAllAppUserFut, delAllWxUserFut, delAllCSFut
        ).setHandler(delAllFut.completer());
        return delAllFut;
    }

    /**
     * 将公众号信息存入redis中
     * @param pub
     * @return
     */
    public Future<String> savePublicToRedis(WXPublic pub) {
       Future<String> fut = Future.future();
        JsonObject msetMapJson = new JsonObject();
        msetMapJson
                .put("id", pub.getId())
                .put("secret", pub.getSecret())
                .put("access_token", pub.getAccessToken())
                .put("developerid", pub.getDeveloperId())
                .put("appid", pub.getAppId())
                .put("name", pub.getName())
                .put("channelType", pub.getChannelType())
                .put("updatetime", pub.getUpdateTime())
                .put("createtime", pub.getCreateTime());
       redis.hmset(REIDS_KEY_WX_PUBLIC + ":" + pub.getDeveloperId(), msetMapJson, as -> {
               if (as.succeeded()) {
                   fut.complete(as.result());
               } else {
                   fut.fail(as.cause());
               }
       });
       return fut;
    }

    /**
     * 获取公众号信息
     * @param developerId
     * @return
     */
    public Future<WXPublic> getPublicInfoByDeveloperId(String developerId) {
        Future<WXPublic> publicFuture = Future.future();
        List<String> keyList =
                Stream.of("id", "secret", "access_token", "appid", "name", "channelType", "updatetime", "createtime")
                .collect(Collectors.toList());
        redis.hmget(REIDS_KEY_WX_PUBLIC + ":" + developerId, keyList, as -> {
            if (as.succeeded()) {
                JsonArray resultArray = as.result();
                if (StringUtils.isNotBlank(resultArray.getString(3))) {
                    WXPublic wxPublic = new WXPublic();

                    wxPublic.setDeveloperId(developerId)
                            .setId(Long.valueOf(resultArray.getString(0)))
                            .setSecret(resultArray.getString(1))
                            .setAccessToken(resultArray.getString(2))
                            .setAppId(resultArray.getString(3))
                            .setName(resultArray.getString(4))
                            .setChannelType(Integer.parseInt(resultArray.getString(5)))
                            .setUpdateTime(resultArray.getString(6))
                            .setCreateTime(resultArray.getString(7));
                    publicFuture.complete(wxPublic);
                } else {
                    publicFuture.complete(null);
                }
            } else {
                publicFuture.fail(as.cause());
            }
        });
        return publicFuture;
    }

    /**
     * web渠道拉黑粉丝/取消拉黑 操作
     *      [注意]这里拉黑失败没有回滚没有异常
     * @param userList
     * @param isPullBlack
     * @return
     */
    public Future<Void> setWebPullBlackCache(List<String> userList, boolean isPullBlack) {
        return Future.future(pullFut -> {
            List<Future> pullFutList = new ArrayList<>();
            for (String userId : userList) {
                Future<Void> pullUserFut = Future.future();
                pullFutList.add(pullUserFut);
                redis.get(REDIS_KEY_WEB_USER_CHAT_ID+":"+userId, webUserAr -> {
                    if (webUserAr.succeeded()) {
                        WebUserMap webUserMap = Json.decodeValue(webUserAr.result(), WebUserMap.class);
                        webUserMap.setPulledBlack(isPullBlack);
                        redis.set(REDIS_KEY_WEB_USER_CHAT_ID+":"+userId, Json.encode(webUserMap), ar -> {
                            pullUserFut.complete();
                        });
                    } else {
                        pullUserFut.complete();
                    }
                });
            }
            CompositeFuture.all(pullFutList)
                    .compose(completed -> {
                        pullFut.complete();
                    }, pullFut);
        });
    }

    /**
     * 微博 拉黑/取消拉黑 缓存操作
     * @param uidList
     * @param isPullBlack
     * @return
     */
    public Future<Void> setWBPullBlackCache(List<String> uidList, boolean isPullBlack) {
        return Future.future(pullFut -> {
            List<Future> pullFutList = new ArrayList<>();
            for (String uid : uidList) {
                Future<Void> pullUserFut = Future.future();
                pullFutList.add(pullUserFut);
                redis.get(REDIS_KEY_APP_USER_TALKID+":"+uid, wbUserAr -> {
                    if (wbUserAr.succeeded() && wbUserAr.result() != null) {
                        AppUser appUser = Json.decodeValue(wbUserAr.result(), AppUser.class);
                        appUser.setPullBlack(isPullBlack?1:0);
                        redis.set(REDIS_KEY_APP_USER_TALKID+":"+uid, Json.encode(appUser), ar -> {
                            System.out.println(Json.encodePrettily(appUser));
                            pullUserFut.complete();
                        });
                    } else {
                        pullUserFut.complete();
                    }
                });
            }
            CompositeFuture.all(pullFutList)
                    .compose(completed -> {
                        pullFut.complete();
                    }, pullFut);
        });
    }

    /**
     * 获取随机的一个app客服账号信息
     * @return
     */
    public Future<CustomerServ> getRandomCS() {
        Future<CustomerServ> randCSFut = Future.future();
        /**
         * TODO：
         *  加入一个判定机制：
         *      （如果随机客服数/总用户数） < 10%    则：查找失败，在上一层新增一个新的随机客服
         */
        redis.srandmember(REDIS_KEY_APP_CS, as -> {
           if (as.succeeded()) {
               String csJson = as.result();
               if (csJson == null) {
                   randCSFut.complete(null);
               } else {
                   randCSFut.complete(new CustomerServ(csJson));
               }
           } else {
               randCSFut.fail(as.cause());
           }
        });
        return randCSFut;
    }

    /**
     * 缓存客服信息
     * @param csbean
     * @return
     */
    public Future<Void> saveCustomerService(CustomerServ csbean) {
        Future<Void> saveCSFut = Future.future();
        String jsonStr = Json.encode(csbean);
        redis.sadd(REDIS_KEY_APP_CS, jsonStr, as -> {
           if (as.succeeded()) {
               saveCSFut.complete();
           } else {
               saveCSFut.fail(as.cause());
           }
        });
        return saveCSFut;
    }

    public void setExpireTime(String key, Future fut) {
        // 设置过期时间
        redis.expire(key, Constants.REDIS_CACHE_MAX_TIME, expireAr -> {
            if (expireAr.failed()) {
                fut.fail(expireAr.cause());
            } else {
                fut.complete();
            }
        });
    }
}
